using System;
using System.IO;
using System.Windows.Forms;

namespace Arbol
{


	// Java GUI packages

	public class AssocRuleMining : Form
	{

		/* ------ FIELDS ------ */

		// Inner class for storing linked list of ARs or CARs as appropriate.

		 public class RuleNode
		{
			private readonly AssocRuleMining outerInstance;

			/// <summary>
			/// Antecedent of AR. </summary>
		protected internal short[] antecedent;
		/// <summary>
		/// Consequent of AR. </summary>
		protected internal short[] consequent;
		/// <summary>
		/// The confidence value associate with the rule represented by this
		/// node. 
		/// </summary>
		internal double confidenceForRule = 0.0;
		/// <summary>
		/// Link to next node </summary>
		public RuleNode next = null;

		/// <summary>
		/// Three argument constructor </summary>
		/// <param name="ante"> the antecedent (LHS) of the AR. </param>
		///   	<param name="cons"> the consequent (RHS) of the AR. </param>
		///   	<param name="confValue"> the associated confidence value.  </param>

		protected internal RuleNode(AssocRuleMining outerInstance, short[] ante, short[] cons, double confValue)
		{
			this.outerInstance = outerInstance;
			antecedent = ante;
			consequent = cons;
			confidenceForRule = confValue;
		}
		}

		// Data structures

		/// <summary>
		/// The reference to start of the rule list. </summary>
		public RuleNode startRulelist = null;
		/// <summary>
		/// 2-D aray to hold input data from data file. Note that within the data
		/// array records are numbered from zero, thus rexcord one has index 0 etc. 
		/// </summary>
		protected internal short[][] dataArray = null;
		/// <summary>
		/// 2-D array used to renumber columns for input data in terms of
		/// frequency of single attributes (reordering will enhance performance
		/// for some ARM algorithms). 
		/// </summary>
		protected internal int[][] conversionArray = null;
		/// <summary>
		/// 1-D array used to reconvert input data column numbers to their
		/// original numbering where the input data has been ordered to enhance
		/// computational efficiency. 
		/// </summary>
		protected internal short[] reconversionArray = null;

		// Constants

		/// <summary>
		/// Minimum support value </summary>
		private const double MIN_SUPPORT = 0.0;
		/// <summary>
		/// Maximum support value </summary>
		private const double MAX_SUPPORT = 100.0;
		/// <summary>
		/// Maximum confidence value </summary>
		private const double MIN_CONFIDENCE = 0.0;
		/// <summary>
		/// Maximum confidence value </summary>
		private const double MAX_CONFIDENCE = 100.0;

		// Command line arguments with default values and associated fields.

		/// <summary>
		/// Command line argument for data file name. </summary>
		protected internal string fileName = null;
		/// <summary>
		/// Command line argument for number of columns. </summary>
		protected internal int numCols = 0;
		/// <summary>
		/// Command line argument for number of rows. </summary>
		protected internal int numRows = 0;
		/// <summary>
		/// Command line argument for % support (default = 20%). </summary>
		protected internal double support = 20.0;
		/// <summary>
		/// Minimum support value in terms of number of rows. <P>Set when input
		/// data is read and the number of records is known,   
		/// </summary>
		protected internal double minSupport = 0;
		/// <summary>
		/// Command line argument for % confidence (default = 80%). </summary>
		protected internal double confidence = 80.0;
		/// <summary>
		/// The number of one itemsets (singletons). </summary>
		protected internal int numOneItemSets = 0;

		// Flags

		/// <summary>
		/// Error flag used when checking command line arguments (default =
		/// <TT>true</TT>). 
		/// </summary>
		protected internal bool errorFlag = true;
		/// <summary>
		/// Input format OK flag( default = <TT>true</TT>). </summary>
		protected internal bool inputFormatOkFlag = true;
		/// <summary>
		/// Flag to indicate whether system has data or not. </summary>
		private bool haveDataFlag = false;
		/// <summary>
		/// Flag to indicate whether input data has been sorted or not. </summary>
		protected internal bool isOrderedFlag = false;
		/// <summary>
		/// Flag to indicate whether input data has been sorted and pruned or
		/// not. 
		/// </summary>
		protected internal bool isPrunedFlag = false;

		// Other fields

		/// <summary>
		/// The input stream. </summary>
		protected internal StreamReader fileInput;
		/// <summary>
		/// The file path </summary>
		protected internal StreamReader filePath = null;

		/* ------ CONSTRUCTORS ------ */

		/// <summary>
		/// Processes command line arguments </summary>
        ///
        public AssocRuleMining()
        { }
		public AssocRuleMining(string[] args)
		{

		// Process command line arguments

		for (int index = 0;index < args.Length;index++)
		{
			idArgument(args[index]);
		}

		// If command line arguments read successfully (errorFlag set to "true")
		// check validity of arguments

		if (errorFlag)
		{
			CheckInputArguments();
		}
		else
		{
			outputMenu();
		}
		}

		/* ------ METHODS ------ */

		/* ---------------------------------------------------------------- */
		/*                                                                  */
		/*                        COMMAND LINE ARGUMENTS                    */
		/*                                                                  */
		/* ---------------------------------------------------------------- */

		/* IDENTIFY ARGUMENT */
		/// <summary>
		/// Identifies nature of individual command line agruments:
		/// -C = confidence, -F = file name, -S = support. 
		/// </summary>

		protected internal virtual void idArgument(string argument)
		{

			if (argument[0] == '-')
			{
			char flag = argument[1];
			argument = argument.Substring(2, argument.Length - 2);
			switch (flag)
			{
			case 'C':
					confidence = Convert.ToDouble(argument);
				break;
				case 'F':
					fileName = argument;
				break;
				case 'S':
					support = Convert.ToDouble(argument);
				break;
				default:
					Console.WriteLine("INPUT ERROR: Unrecognise command " + "line  argument -" + flag + argument);
				errorFlag = false;
			break;
			}
			}
			else
			{
			Console.WriteLine("INPUT ERROR: All command line arguments " + "must commence with a '-' character (" + argument + ")");
				errorFlag = false;
			}
		}

		/* CHECK INPUT ARGUMENTS */
		/// <summary>
		/// Invokes methods to check values associate with command line
		/// arguments 
		/// </summary>

		protected internal virtual void CheckInputArguments()
		{

		// Check support and confidence input
		checkSupportAndConfidence();

		// Check file name
		checkFileName();

		// Return
		if (errorFlag)
		{
			outputSettings();
		}
		else
		{
			outputMenu();
		}
		}

		/* CHECK SUPPORT AND CONFIDANCE */
		/// <summary>
		/// Checks support and confidence input % values, if either is out of
		/// bounds then <TT>errorFlag</TT> set to <TT>false</TT>. 
		/// </summary>

		protected internal virtual void checkSupportAndConfidence()
		{

		// Check Support
		if ((support < MIN_SUPPORT) || (support > MAX_SUPPORT))
		{
			Console.WriteLine("INPUT ERROR: Support must be specified " + "as a percentage (" + MIN_SUPPORT + " - " + MAX_SUPPORT + ")");
			errorFlag = false;
		}
		// Check confidence
		if ((confidence < MIN_CONFIDENCE) || (confidence > MAX_CONFIDENCE))
		{
			Console.WriteLine("INPUT ERROR: Confidence must be " + "specified as a percentage (" + MIN_CONFIDENCE + " - " + MAX_CONFIDENCE + ")");
			errorFlag = false;
		}
		}

		/* CHECK FILE NAME */
		/// <summary>
		/// Checks if data file name provided, if not <TT>errorFlag</TT> set
		/// to <TT>false</TT>. 
		/// </summary>

		protected internal virtual void checkFileName()
		{
		if (fileName == null)
		{
			Console.WriteLine("INPUT ERROR: Must specify file name (-F)");
				errorFlag = false;
		}
		}

		/* ---------------------------------------------------------------- */
		/*                                                                  */
		/*                     READ INPUT DATA FROM FILE                    */
		/*                                                                  */
		/* ---------------------------------------------------------------- */

		/* INPUT DATA SET */

		/// <summary>
		/// Commences process of getting input data (GUI version also exists). </summary>

		public virtual void inputDataSet()
		{
			// Read the file
		readFile();

		// Check ordering (only if input format is OK)
		if (inputFormatOkFlag)
		{
			if (checkOrdering())
			{
					Console.WriteLine("Number of records = " + numRows);
			countNumCols();
			Console.WriteLine("Number of columns = " + numCols);
			minSupport = (numRows * support) / 100.0;
				Console.WriteLine("Min support       = " + twoDecPlaces(minSupport) + " (records)");
			}
			else
			{
				Console.WriteLine("Error reading file: " + fileName + "\n");
				closeFile();
				Environment.Exit(1);
			}
		}
		}

		/* READ FILE */

		/// <summary>
		/// Reads input data from file specified in command line argument
		/// <TT>fileName</TT>. <P>Note that it is assumed
		/// that no empty records are included. Proceeds as follows:
		/// <OL>
		/// <LI>Gets number of rows (lines) in file, checking format of each line
		/// (space separated integers), if incorrectly formatted line found
		/// <TT>inputFormatOkFlag</TT> set to <TT>false</TT>.
		/// <LI>Dimensions input array.
		/// <LI>Reads data
		/// </OL> 
		/// </summary>

		protected internal virtual void readFile()
		{
			try
			{
			// Dimension data structure
			inputFormatOkFlag = true;
			numRows = getNumberOfLines(fileName);
			if (inputFormatOkFlag)
			{
				dataArray = new short[numRows][];
				// Read file
			Console.WriteLine("Reading input file: " + fileName);
				readInputDataSet();
			}
			else
			{
				Console.WriteLine("Error reading file: " + fileName + "\n");
			}
			}
		catch (IOException ioException)
		{
			Console.WriteLine("Error reading File: "+ioException.Message);
			closeFile();
			Environment.Exit(1);
		}
		}

		/* GET NUMBER OF LINES */

		/// <summary>
		/// Gets number of lines/records in input file and checks format of each
		/// line. </summary>
		/// <param name="nameOfFile"> the filename of the file to be opened. </param>
		/// <returns> the number of rows in the given file.  </returns>

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected int getNumberOfLines(String nameOfFile) throws IOException
		protected internal virtual int getNumberOfLines(string nameOfFile)
		{
			int counter = 0;

		// Open the file
		if (filePath == null)
		{
			openFileName(nameOfFile);
		}
		else
		{
			openFilePath();
		}

		// Loop through file incrementing counter
		// get first row.
		string line = fileInput.ReadLine();
		while (line != null)
		{
			checkLine(counter + 1,line);
			String[] dataLine = line.Split(' ');
            int numberOfTokens = dataLine.Length;
			if (numberOfTokens == 0)
			{
				break;
			}
			counter++;
				line = fileInput.ReadLine();
		}

		// Close file and return
			closeFile();
		return (counter);
		}

		/* CHECK LINE */

		/// <summary>
		/// Check whether given line from input file is of appropriate format
		/// (space separated integers), if incorrectly formatted line found
		/// <TT>inputFormatOkFlag</TT> set to <TT>false</TT>. </summary>
		/// <param name="counter"> the line number in the input file. </param>
		/// <param name="str"> the current line from the input file.  </param>

		protected internal virtual void checkLine(int counter, string str)
		{

			for (int index = 0;index < str.Length;index++)
			{
				if (!char.IsDigit(str[index]) && !char.IsWhiteSpace(str[index]))
				{
                    MessageBox.Show(this, "FILE INPUT ERROR:\n" + "charcater on line " + counter + " is not a digit or white space");
			inputFormatOkFlag = false;
			haveDataFlag = false;
			break;
				}
			}
		}

		/* READ INPUT DATA SET */
		/// <summary>
		/// Reads input data from file specified in command line argument. </summary>

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public void readInputDataSet() throws IOException
		public virtual void readInputDataSet()
		{
			readInputDataSet(fileName);
		}

		/* READ INPUT DATA SET */
		/// <summary>
		/// Reads input data from given file. </summary>
		/// <param name="fName"> the given file name.   </param>

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected void readInputDataSet(String fName) throws IOException
		protected internal virtual void readInputDataSet(string fName)
		{
		int rowIndex = 0;

		// Open the file
		if (filePath == null)
		{
			openFileName(fName);
		}
		else
		{
			openFilePath();
		}

		// Get first row.
		string line = fileInput.ReadLine();

		// Preocess rest of file
		while (line != null)
		{
			// Process line
			if (!processInputLine(line,rowIndex))
			{
				break;
			}
			// Increment first (row) index in 2-D data array
			rowIndex++;
			// get next line
				line = fileInput.ReadLine();
		}

		// Close file
		closeFile();
		}

		/* READ INPUT DATA SEGMENT */
		/// <summary>
		/// Reads input data segment from a given file and places content into to
		/// the data array structure commencing at the given row index, continues until
		/// the end index is rerached. </summary>
		/// <param name="fName"> the given file name. </param>
		/// <param name="startRowIndex"> the given row strat index. </param>
		/// <param name="endRowIndex"> the given row end index.  </param>

//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected void readInputDataSetSeg(String fName, int startRowIndex, int endRowIndex) throws IOException
		protected internal virtual void readInputDataSetSeg(string fName, int startRowIndex, int endRowIndex)
		{
		//int rowIndex=startRowIndex;

		// Open the file
		if (filePath == null)
		{
			openFileName(fName);
		}
		else
		{
			openFilePath();
		}

		// get first row.
		string line = fileInput.ReadLine();
		for (int index = startRowIndex;index < endRowIndex;index++)
		{
			// Process line
			processInputLine(line,index);
			// get next line
				line = fileInput.ReadLine();
		}

		// Close file
		closeFile();
		}

		/* PROCESS INPUT LINE */

		/// <summary>
		///	Processes a line from the input file and places it in the
		/// <TT>dataArray</TT> structure. </summary>
		/// <param name="line"> the line to be processed from the input file </param>
		/// <param name="rowIndex"> the index to the current location in the
		/// <TT>dataArray</TT> structure.
		/// @rerturn true if successfull, false if empty record.  </param>

		private bool processInputLine(string line, int rowIndex)
		{
			// If no line return false
		if (line == null)
		{
			return (false);
		}

		// Tokenise line
		String[] dataLine = line.Split(' ');
        int numberOfTokens = dataLine.Length;

		// Empty line or end of file found, return false
		if (numberOfTokens == 0)
		{
			return (false);
		}

		// Convert input string to a sequence of short integers
		short[] code = binConversion(dataLine,numberOfTokens);

		// Dimension row in 2-D dataArray
		int codeLength = code.Length;
		dataArray[rowIndex] = new short[codeLength];
		// Assign to elements in row
		for (int colIndex = 0;colIndex < codeLength;colIndex++)
		{
					dataArray[rowIndex][colIndex] = code[colIndex];
		}

		// Return
		return (true);
		}

		/* CHECK DATASET ORDERING */
		/// <summary>
		/// Checks that data set is ordered correctly. </summary>
		/// <returns> true if appropriate ordering, false otherwise.  </returns>

		protected internal virtual bool checkOrdering()
		{
			bool result = true;

		// Loop through input data
		for (int index = 0;index < dataArray.Length;index++)
		{
			if (!checkLineOrdering(index + 1,dataArray[index]))
			{
			haveDataFlag = false;
			result = false;
			}
		}

		// Return
		return (result);
		}

		/* CHECK LINE ORDERING */
		/// <summary>
		/// Checks whether a given line in the input data is in numeric sequence. </summary>
		/// <param name="lineNum"> the line number. </param>
		/// <param name="itemSet"> the item set represented by the line </param>
		/// <returns> true if OK and false otherwise.  </returns>

		protected internal virtual bool checkLineOrdering(int lineNum, short[] itemSet)
		{
			for (int index = 0;index < itemSet.Length - 1;index++)
			{
			if (itemSet[index] >= itemSet[index + 1])
			{
                MessageBox.Show(this, "FILE FORMAT ERROR:\n" + "Attribute data in line " + lineNum + " not in numeric order");
			return (false);
			}
			}

		// Default return
		return (true);
		}

		/* COUNT NUMBER OF COLUMNS */
		/// <summary>
		/// Counts number of columns represented by input data. </summary>

		protected internal virtual void countNumCols()
		{
			int maxAttribute = 0;

		// Loop through data array
			for (int index = 0;index < dataArray.Length;index++)
			{
			int lastIndex = dataArray[index].Length - 1;
			if (dataArray[index][lastIndex] > maxAttribute)
			{
					maxAttribute = dataArray[index][lastIndex];
			}
			}

		numCols = maxAttribute;
		numOneItemSets = numCols; // default value only
		}

		/* OPEN FILE NAME */
		/// <summary>
		/// Opens input file using fileName (instance field). </summary>
		/// <param name="nameOfFile"> the filename of the file to be opened.  </param>

		protected internal virtual void openFileName(string nameOfFile)
		{
		try
		{
			// Open file
			//FileReader file = new FileReader(nameOfFile);
            fileInput = new StreamReader(nameOfFile);
		}
		catch (IOException ioException)
		{
            MessageBox.Show(this, "Error Opening File: "+ioException.Message);
			Environment.Exit(1);
		}
		}

		/* OPEN FILE PATH */
		/// <summary>
		/// Opens file using filePath (instance field). </summary>

		protected internal virtual void openFilePath()
		{
		try
		{
			// Open file
			//FileReader file = new FileReader(filePath);
			fileInput = new StreamReader(filePath.ToString());
		}
		catch (IOException ioException)
		{
            MessageBox.Show(this, "Error Opening File: "+ioException.Message);
			Environment.Exit(1);
		}
		}

		/* CLOSE FILE */
		/// <summary>
		/// Close file fileName (instance field). </summary>

		protected internal virtual void closeFile()
		{
			if (fileInput != null)
			{
			try
			{
				fileInput.Close();
			}
			catch (IOException ioException)
			{
                MessageBox.Show(this, "Error Closeing File: "+ioException.Message);
				Environment.Exit(1);
			}
			}
		}

		/* BINARY CONVERSION. */

		/// <summary>
		/// Produce an item set (array of elements) from input
		/// line. </summary>
		/// <param name="dataLine"> row from the input data file </param>
		/// <param name="numberOfTokens"> number of items in row </param>
		/// <returns> 1-D array of short integers representing attributes in input
		/// row  </returns>

		protected internal virtual short[] binConversion(String[] dataLine, int numberOfTokens)
		{
			short number;
		short[] newItemSet = null;

		// Load array

		for (int tokenCounter = 0;tokenCounter < numberOfTokens;tokenCounter++)
		{
				number = (short)((dataLine.GetValue(tokenCounter)));
			newItemSet = realloc1(newItemSet,number);
		}

		// Return itemSet

		return (newItemSet);
		}

		/* ---------------------------------------------------------------- */
		/*                                                                  */
		/*        REORDER DATA SET ACCORDING TO ATTRIBUTE FREQUENCY         */
		/*                                                                  */
		/* ---------------------------------------------------------------- */

		/* REORDER INPUT DATA: */

		/// <summary>
		/// Reorders input data according to frequency of
		/// single attributes. <P> Example, given the data set:
		/// <PRE>
		/// 1 2 5
		/// 1 2 3
		/// 2 4 5
		/// 1 2 5
		/// 2 3 5
		/// </PRE>
		/// This would produce a countArray (ignore index 0):
		/// <PRE>
		/// +---+---+---+---+---+---+
		/// |   | 1 | 2 | 3 | 4 | 5 |
		/// +---+---+---+---+---+---+
		/// |   | 3 | 5 | 2 | 1 | 4 |
		/// +---+---+---+---+---+---+
		/// </PRE>
		/// Which sorts to:
		/// <PRE>
		/// +---+---+---+---+---+---+
		/// |   | 2 | 5 | 1 | 3 | 4 |
		/// +---+---+---+---+---+---+
		/// |   | 5 | 4 | 3 | 2 | 1 |
		/// +---+---+---+---+---+---+
		/// </PRE>
		/// Giving rise to the conversion Array of the form (no index 0):
		/// <PRE>
		/// +---+---+---+---+---+---+
		/// |   | 3 | 1 | 4 | 5 | 2 |
		/// +---+---+---+---+---+---+
		/// |   | 3 | 5 | 2 | 1 | 4 |
		/// +---+---+---+---+---+---+
		/// </PRE>
		/// Note that the second row here are the counts which no longer play a role
		/// in the conversion exercise. Thus to the new column number for column 1 is 
		/// column 3 (i.e. the first vale at index 1). The reconversion array of the 
		/// form:
		/// <PRE>
		/// +---+---+---+---+---+---+
		/// |   | 2 | 5 | 1 | 3 | 4 |
		/// +---+---+---+---+---+---+		
		/// </PRE> 
		/// </summary>

		public virtual void idInputDataOrdering()
		{

		// Count singles and store in countArray;	     
			int[][] countArray = countSingles();

		// Bubble sort count array on support value (second index)	
		orderCountArray(countArray);

			// Define conversion and reconversion arrays      
		defConvertArrays(countArray);

		// Set sorted flag
		isOrderedFlag = true;
		}

		/* COUNT SINGLES */

		/// <summary>
		/// Counts number of occurrences of each single attribute in the
		/// input data. </summary>
		/// <returns> 2-D array where first row represents column numbers
		/// and second row represents support counts.  </returns>

		protected internal virtual int[][] countSingles()
		{

		// Dimension and initialize count array

//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: int[][] countArray = new int[numCols+1][2];
		int[][] countArray = RectangularArrays.ReturnRectangularIntArray(numCols + 1, 2);
		for (int index = 0;index < countArray.Length;index++)
		{
			countArray[index][0] = index;
			countArray[index][1] = 0;
		}

		// Step through input data array counting singles and incrementing
		// appropriate element in the count array

		for (int rowIndex = 0;rowIndex < dataArray.Length;rowIndex++)
		{
			 if (dataArray[rowIndex] != null)
			 {
			for (int colIndex = 0;colIndex < dataArray[rowIndex].Length; colIndex++)
			{
					countArray[dataArray[rowIndex][colIndex]][1]++;
			}
			 }
		}

		// Return

		return (countArray);
		}

		/* ORDER COUNT ARRAY */

		/// <summary>
		/// Bubble sorts count array produced by <TT>countSingles</TT> method
		/// so that array is ordered according to frequency of single items. </summary>
		/// <param name="countArray"> The 2-D array returned by the <TT>countSingles</TT> 
		/// method.  </param>

		private void orderCountArray(int[][] countArray)
		{
			int attribute, quantity;
			bool isOrdered;
			int index;

			do
			{
			isOrdered = true;
				index = 1;
				while (index < (countArray.Length - 1))
				{
					if (countArray[index][1] >= countArray[index + 1][1])
					{
						index++;
					}
				else
				{
					isOrdered = false;
						// Swap
				attribute = countArray[index][0];
				quantity = countArray[index][1];
					countArray[index][0] = countArray[index + 1][0];
					countArray[index][1] = countArray[index + 1][1];
						countArray[index + 1][0] = attribute;
					countArray[index + 1][1] = quantity;
					// Increment index
				index++;
				}
				}
			} while (isOrdered == false);
		}

		/* ORDER FIRST N ELEMENTS IN COUNT ARRAY */

		/// <summary>
		/// Bubble sorts first N elements in count array produced by
		/// <TT>countSingles</TT> method so that array is ordered according to
		/// frequency of single items. <P> Used when ordering classification input
		/// data. </summary>
		/// <param name="countArray"> The 2-D array returned by the <TT>countSingles</TT>
		/// method. </param>
		/// <param name="endIndex"> the index of the Nth element.  </param>

		protected internal virtual void orderFirstNofCountArray(int[][] countArray, int endIndex)
		{
			int attribute, quantity;
			bool isOrdered;
			int index;

			do
			{
			isOrdered = true;
				index = 1;
				while (index < endIndex)
				{
					if (countArray[index][1] >= countArray[index + 1][1])
					{
						index++;
					}
				else
				{
					isOrdered = false;
						// Swap
				attribute = countArray[index][0];
				quantity = countArray[index][1];
					countArray[index][0] = countArray[index + 1][0];
					countArray[index][1] = countArray[index + 1][1];
						countArray[index + 1][0] = attribute;
					countArray[index + 1][1] = quantity;
					// Increment index
				index++;
				}
				}
			} while (isOrdered == false);
		}

		/* DEFINE CONVERSION ARRAYS: */

		/// <summary>
		/// Defines conversion and reconversion arrays. </summary>
		/// <param name="countArray"> The 2-D array sorted by the <TT>orderCcountArray</TT>
		/// method. </param>

		protected internal virtual void defConvertArrays(int[][] countArray)
		{

		// Dimension arrays

//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: conversionArray = new int[numCols+1][2];
		conversionArray = RectangularArrays.ReturnRectangularIntArray(numCols + 1, 2);
			reconversionArray = new short[numCols + 1];

		// Assign values

		for (int index = 1;index < countArray.Length;index++)
		{
				conversionArray[countArray[index][0]][0] = index;
				conversionArray[countArray[index][0]][1] = countArray[index][1];
			reconversionArray[index] = (short) countArray[index][0];
		}

		// Diagnostic ouput if desired
		//outputConversionArrays();
		}

		/* RECAST INPUT DATA. */

		/// <summary>
		/// Recasts the contents of the data array so that each record is ordered
		/// according to conversion array.
		/// <P>Proceed as follows:
		/// 
		/// 1) For each record in the data array. Create an empty new itemSet array.
		/// 2) Place into this array attribute/column numbers that correspond to the
		///   appropriate equivalents contained in the conversion array.
		/// 3) Reorder this itemSet and return into the data array. 
		/// </summary>

		public virtual void recastInputData()
		{
			short[] itemSet;
		int attribute;

		// Step through data array using loop construct

			for (int rowIndex = 0;rowIndex < dataArray.Length;rowIndex++)
			{
			itemSet = new short[dataArray[rowIndex].Length];
			// For each element in the itemSet replace with attribute number
			// from conversion array
			for (int colIndex = 0;colIndex < dataArray[rowIndex].Length;colIndex++)
			{
				attribute = dataArray[rowIndex][colIndex];
			itemSet[colIndex] = (short) conversionArray[attribute][0];
			}
			// Sort itemSet and return to data array
			sortItemSet(itemSet);
			dataArray[rowIndex] = itemSet;
			}
		}

		/* RECAST INPUT DATA AND REMOVE UNSUPPORTED SINGLE ATTRIBUTES. */

		/// <summary>
		/// Recasts the contents of the data array so that each record is
		/// ordered according to ColumnCounts array and excludes non-supported
		/// elements. <P> Proceed as follows:
		/// 
		/// 1) For each record in the data array. Create an empty new itemSet array.
		/// 2) Place into this array any column numbers in record that are
		///   supported at the index contained in the conversion array.
		/// 3) Assign new itemSet back into to data array 
		/// </summary>

		public virtual void recastInputDataAndPruneUnsupportedAtts()
		{
			short[] itemSet;
		int attribute;

		// Step through data array using loop construct

			for (int rowIndex = 0;rowIndex < dataArray.Length;rowIndex++)
			{
			// Check for empty row
			if (dataArray[rowIndex] != null)
			{
				itemSet = null;
				// For each element in the current record find if supported with
				// reference to the conversion array. If so add to "itemSet".
				for (int colIndex = 0;colIndex < dataArray[rowIndex].Length;colIndex++)
				{
					attribute = dataArray[rowIndex][colIndex];
				// Check support
				if (conversionArray[attribute][1] >= minSupport)
				{
					itemSet = reallocInsert(itemSet, (short) conversionArray[attribute][0]);
				}
				}
				// Return new item set to data array
				dataArray[rowIndex] = itemSet;
			}
			}

		// Set isPrunedFlag (used with GUI interface)
		isPrunedFlag = true;
		// Reset number of one item sets field
		numOneItemSets = NumSupOneItemSets;
		}

		/* GET NUM OF SUPPORTE ONE ITEM SETS */
		/// <summary>
		/// Gets number of supported single item sets (note this is not necessarily
		/// the same as the number of columns/attributes in the input set). </summary>
		/// <returns> Number of supported 1-item sets  </returns>

		protected internal virtual int NumSupOneItemSets
		{
			get
			{
				int counter = 0;
    
			// Step through conversion array incrementing counter for each
			// supported element found
    
			for (int index = 1;index < conversionArray.Length;index++)
			{
				if (conversionArray[index][1] >= minSupport)
				{
					counter++;
				}
			}
    
			// Return
    
			return (counter);
			}
		}

		/* RESIZE INPUT DATA */

		/// <summary>
		/// Recasts the input data sets so that only N percent is used. </summary>
		/// <param name="percentage"> the percentage of the current input data that is to form
		/// the new input data set (number between 0 and 100).  </param>

		public virtual void resizeInputData(double percentage)
		{
		// Redefine number of rows
		numRows = (int)((double) numRows * (percentage / 100.0));
			Console.WriteLine("Recast input data, new num rows = " + numRows);

		// Dimension and populate training set.
		short[][] trainingSet = new short[numRows][];
		for (int index = 0;index < numRows;index++)
		{
					trainingSet[index] = dataArray[index];
		}

		// Assign training set label to input data set label.
		dataArray = trainingSet;

		// Determine new minimum support threshold value

		minSupport = (numRows * support) / 100.0;
		}

		/// <summary>
		/// Reconverts given item set according to contents of reconversion array. </summary>
		/// <param name="itemSet"> the fgiven itemset. </param>
		/// <returns> the reconverted itemset.  </returns>

		protected internal virtual short[] reconvertItemSet(short[] itemSet)
		{
			// If no conversion return orginal item set
		if (reconversionArray == null)
		{
			return (itemSet);
		}

		// If item set null return null
		if (itemSet == null)
		{
			return (null);
		}

		// Define new item set
		short[] newItemSet = new short[itemSet.Length];

		// Copy
		for (int index = 0;index < newItemSet.Length;index++)
		{
			newItemSet[index] = reconversionArray[itemSet[index]];
		}

		// Return
		return (newItemSet);
		}

		/// <summary>
		/// Reconvert single item if appropriate. </summary>
		/// <param name="item"> the given item (attribute). </param>
		/// <returns> the reconvered item.  </returns>

		protected internal virtual short reconvertItem(short item)
		{
			// If no conversion return orginal item
		if (reconversionArray == null)
		{
			return (item);
		}

		// Otherwise rerturn reconvert item
		return (reconversionArray[item]);
		}

		/* -------------------------------------------------------------- */
		/*                                                                */
		/*        RULE LINKED LIST ORDERED ACCORDING TO CONFIDENCE        */
		/*                                                                */
		/* -------------------------------------------------------------- */

		/* Methods for inserting rules into a linked list of rules ordered
		according to confidence (most confident first). Each rule described in
		terms of 3 fields: 1) Antecedent (an item set), 2) a consequent (an item
		set), 3) a confidence value (double). <P> The support field is not used. */

		/* INSERT (ASSOCIATION/CLASSIFICATION) RULE INTO RULE LINKED LIST (ORDERED
		ACCORDING CONFIDENCE). */

		/// <summary>
		/// Inserts an (association/classification) rule into the linkedlist of
		/// rules pointed at by <TT>startRulelist</TT>. <P> The list is ordered so that
		/// rules with highest confidence are listed first. If two rules have the same
		/// confidence the new rule will be placed after the existing rule. Thus, if
		/// using an Apriori approach to generating rules, more general rules will
		/// appear first in the list with more specific rules (i.e. rules with a larger
		/// antecedent) appearing later as the more general rules will be generated
		/// first. </summary>
		/// <param name="antecedent"> the antecedent (LHS) of the rule. </param>
		/// <param name="consequent"> the consequent (RHS) of the rule. </param>
		/// <param name="confidenceForRule"> the associated confidence value.   </param>

		protected internal virtual void insertRuleintoRulelist(short[] antecedent, short[] consequent, double confidenceForRule)
		{

		// Create new node
		RuleNode newNode = new RuleNode(this, antecedent,consequent, confidenceForRule);

		// Empty list situation
		if (startRulelist == null)
		{
			startRulelist = newNode;
			return;
		}

		// Add new node to start
		if (confidenceForRule > startRulelist.confidenceForRule)
		{
			newNode.next = startRulelist;
			startRulelist = newNode;
			return;
		}

		// Add new node to middle
		RuleNode markerNode = startRulelist;
		RuleNode linkRuleNode = startRulelist.next;
		while (linkRuleNode != null)
		{
			if (confidenceForRule > linkRuleNode.confidenceForRule)
			{
				markerNode.next = newNode;
			newNode.next = linkRuleNode;
			return;
			}
			markerNode = linkRuleNode;
			linkRuleNode = linkRuleNode.next;
		}

		// Add new node to end
		markerNode.next = newNode;
		}

		/* ----------------------------------------------- */
		/*                                                 */
		/*        ITEM SET INSERT AND ADD METHODS          */
		/*                                                 */
		/* ----------------------------------------------- */

		/* REALLOC INSERT */

		/// <summary>
		/// Resizes given item set so that its length is increased by one
		/// and new element inserted. </summary>
		/// <param name="oldItemSet"> the original item set </param>
		/// <param name="newElement"> the new element/attribute to be inserted </param>
		/// <returns> the combined item set  </returns>

		protected internal virtual short[] reallocInsert(short[] oldItemSet, short newElement)
		{

		// No old item set

		if (oldItemSet == null)
		{
			short[] newItemSet = new short[] {newElement};
			return (newItemSet);
		}

		// Otherwise create new item set with length one greater than old
		// item set

		int oldItemSetLength = oldItemSet.Length;
		short[] newItemSet2 = new short[oldItemSetLength + 1];

		// Loop

		int index1;
		for (index1 = 0;index1 < oldItemSetLength;index1++)
		{
			if (newElement < oldItemSet[index1])
			{
			newItemSet2[index1] = newElement;
			// Add rest
			for (int index2 = index1 + 1;index2 < newItemSet2.Length;index2++)
			{
					newItemSet2[index2] = oldItemSet[index2 - 1];
			}
			return (newItemSet2);
			}
			else
			{
				newItemSet2[index1] = oldItemSet[index1];
			}
		}

		// Add to end

		newItemSet2[newItemSet2.Length - 1] = newElement;

		// Return new item set

		return (newItemSet2);
		}

		/* REALLOC 1 */

		/// <summary>
		/// Resizes given item set so that its length is increased by one
		/// and appends new element (identical to append method) </summary>
		/// <param name="oldItemSet"> the original item set </param>
		/// <param name="newElement"> the new element/attribute to be appended </param>
		/// <returns> the combined item set  </returns>

		protected internal virtual short[] realloc1(short[] oldItemSet, short newElement)
		{

		// No old item set

		if (oldItemSet == null)
		{
			short[] newItemSet = new short[] {newElement};
			return (newItemSet);
		}

		// Otherwise create new item set with length one greater than old
		// item set

		int oldItemSetLength = oldItemSet.Length;
		short[] newItemSet2 = new short[oldItemSetLength + 1];

		// Loop

		int index;
		for (index = 0;index < oldItemSetLength;index++)
		{
			newItemSet2[index] = oldItemSet[index];
		}
		newItemSet2[index] = newElement;

		// Return new item set

		return (newItemSet2);
		}

		/* REALLOC 2 */

		/// <summary>
		/// Resizes given array so that its length is increased by one element
		/// and new element added to front </summary>
		/// <param name="oldItemSet"> the original item set </param>
		/// <param name="newElement"> the new element/attribute to be appended </param>
		/// <returns> the combined item set  </returns>

		protected internal virtual short[] realloc2(short[] oldItemSet, short newElement)
		{

		// No old array

		if (oldItemSet == null)
		{
			short[] newItemSet = new short[] {newElement};
			return (newItemSet);
		}

		// Otherwise create new array with length one greater than old array

		int oldItemSetLength = oldItemSet.Length;
		short[] newItemSet2 = new short[oldItemSetLength + 1];

		// Loop

		newItemSet2[0] = newElement;
		for (int index = 0;index < oldItemSetLength;index++)
		{
			newItemSet2[index + 1] = oldItemSet[index];
		}

		// Return new array

		return (newItemSet2);
		}

		/* --------------------------------------------- */
		/*                                               */
		/*            ITEM SET DELETE METHODS            */
		/*                                               */
		/* --------------------------------------------- */

		/* REMOVE ELEMENT N */

		/// <summary>
		/// Removes the nth element/attribute from the given item set. </summary>
		/// <param name="oldItemSet"> the given item set. </param>
		/// <param name="n"> the index of the element to be removed (first index is 0). </param>
		/// <returns> Revised item set with nth element removed.  </returns>

		protected internal virtual short[] removeElementN(short[] oldItemSet, int n)
		{
			if (oldItemSet.Length <= n)
			{
				return (oldItemSet);
			}
		else
		{
			short[] newItemSet = new short[oldItemSet.Length - 1];
			for (int index = 0;index < n;index++)
			{
				newItemSet[index] = oldItemSet[index];
			}
			for (int index = n + 1;index < oldItemSet.Length;index++)
			{
							newItemSet[index - 1] = oldItemSet[index];
			}
			return (newItemSet);
		}
		}

		/* ---------------------------------------------------------------- */
		/*                                                                  */
		/*              METHODS TO RETURN SUBSETS OF ITEMSETS               */
		/*                                                                  */
		/* ---------------------------------------------------------------- */

		/* COMPLEMENT */

		/// <summary>
		/// Returns complement of first itemset with respect to second itemset. </summary>
		/// <param name="itemSet1"> the first given item set. </param>
		/// <param name="itemSet2"> the second given item set. </param>
		/// <returns> complement if <TT>itemSet1</TT> in <TT>itemSet2</TT>.  </returns>

		protected internal virtual short[] complement(short[] itemSet1, short[] itemSet2)
		{
			int lengthOfComp = itemSet2.Length - itemSet1.Length;

		// Return null if no complement
		if (lengthOfComp < 1)
		{
			return (null);
		}

		// Otherwsise define combination array and determine complement
		short[] complement = new short[lengthOfComp];
		int complementIndex = 0;
		for (int index = 0;index < itemSet2.Length;index++)
		{
			// Add to combination if not in first itemset
			if (notMemberOf(itemSet2[index],itemSet1))
			{
				complement[complementIndex] = itemSet2[index];
			complementIndex++;
			}
		}

		// Return
		return (complement);
		}

		/* --------------------------------------- */
		/*                                         */
		/*             SORT ITEM SET               */
		/*                                         */
		/* --------------------------------------- */

		/* SORT ITEM SET: Given an unordered itemSet, sort the set */

		/// <summary>
		/// Sorts an unordered item set. </summary>
		/// <param name="itemSet"> the given item set.  </param>

		protected internal virtual void sortItemSet(short[] itemSet)
		{
			short temp;
			bool isOrdered;
			int index;

			do
			{
			isOrdered = true;
				index = 0;
				while (index < (itemSet.Length - 1))
				{
					if (itemSet[index] <= itemSet[index + 1])
					{
						index++;
					}
				else
				{
					isOrdered = false;
						// Swap
				temp = itemSet[index];
					itemSet[index] = itemSet[index + 1];
						itemSet[index + 1] = temp;
					// Increment index
				index++;
				}
				}
			} while (isOrdered == false);
		}

		/* ----------------------------------------------------- */
		/*                                                       */
		/*             BOOLEAN ITEM SET METHODS ETC.             */
		/*                                                       */
		/* ----------------------------------------------------- */    

		/* NOT MEMBER OF */

		/// <summary>
		/// Checks whether a particular element/attribute identified by a 
		/// column number is not a member of the given item set. </summary>
		/// <param name="number"> the attribute identifier (column number). </param>
		/// <param name="itemSet"> the given item set. </param>
		/// <returns> true if first argument is not a member of itemSet, and false 
		/// otherwise  </returns>

		protected internal virtual bool notMemberOf(short number, short[] itemSet)
		{

		// Loop through itemSet

		for (int index = 0;index < itemSet.Length;index++)
		{
			if (number < itemSet[index])
			{
				return (true);
			}
			if (number == itemSet[index])
			{
				return (false);
			}
		}

		// Got to the end of itemSet and found nothing, return true

		return (true);
		}

		/* -------------------------------------------------- */
		/*                                                    */
		/*                ITEM SET COMBINATIONS               */
		/*                                                    */
		/* -------------------------------------------------- */ 

		/* COMBINATIONS */

		/// <summary>
		/// Invokes <TT>combinations</TT> method to calculate all possible 
		/// combinations of a given item set. <P>
		/// For example given the item set [1,2,3] this will result in the
		/// combinations[[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]. </summary>
		/// <param name="inputSet"> the given item set. </param>
		/// <returns> array of arrays representing all possible combinations (may be null
		/// if no combinations).  </returns>

		protected internal virtual short[][] combinations(short[] inputSet)
		{
		if (inputSet == null)
		{
			return (null);
		}
		else
		{
			short[][] outputSet = new short[getCombinations(inputSet)][];
			combinations(inputSet,0,null,outputSet,0);
			return (outputSet);
		}
		}

		/// <summary>
		/// Recursively calculates all possible combinations of a given item
		/// set. </summary>
		/// <param name="inputSet"> the given item set. </param>
		/// <param name="inputIndex"> the index within the input set marking current
		/// element under consideration (0 at start). </param>
		/// <param name="sofar"> the part of a combination determined sofar during the
		/// recursion (null at start). </param>
		/// <param name="outputSet"> the combinations collected so far, will hold all
		/// combinations when recursion ends. </param>
		/// <param name="outputIndex"> the current location in the output set. </param>
		/// <returns> revised output index.  </returns>

		private int combinations(short[] inputSet, int inputIndex, short[] sofar, short[][] outputSet, int outputIndex)
		{
			short[] tempSet;
		int index = inputIndex;

			// Loop through input array

		while (index < inputSet.Length)
		{
				tempSet = realloc1(sofar,inputSet[index]);
				outputSet[outputIndex] = tempSet;
			outputIndex = combinations(inputSet,index + 1, copyItemSet(tempSet),outputSet,outputIndex + 1);
				index++;
		}

			// Return

			return (outputIndex);
		}

		/* GET COMBINATTIONS */

		/// <summary>
		/// Gets the number of possible combinations of a given item set. </summary>
		/// <param name="set"> the given item set. </param>
		/// <returns> number of possible combinations.  </returns>

		private int getCombinations(short[] set)
		{
          //  int counter = 0;
            int numComb = 0 ;

		numComb = (int) Math.Pow(2.0,set.Length) - 1;

			// Return

			return (numComb);
		}

		/* ---------------------------------------------------------------- */
		/*                                                                  */
		/*                            MISCELANEOUS                          */
		/*                                                                  */
		/* ---------------------------------------------------------------- */

		/* COPY ITEM SET */

		/// <summary>
		/// Makes a copy of a given itemSet. </summary>
		/// <param name="itemSet"> the given item set. </param>
		/// <returns> copy of given item set.  </returns>

		protected internal virtual short[] copyItemSet(short[] itemSet)
		{

		// Check whether there is a itemSet to copy
		if (itemSet == null)
		{
			return (null);
		}

		// Do copy and return
		short[] newItemSet = new short[itemSet.Length];
		for (int index = 0;index < itemSet.Length;index++)
		{
			newItemSet[index] = itemSet[index];
		}

		// Return
		return (newItemSet);
		}

		/* ------------------------------------------------- */
		/*                                                   */
		/*                   OUTPUT METHODS                  */
		/*                                                   */
		/* ------------------------------------------------- */

		/* ----------------- */	
		/* OUTPUT DATA TABLE */  
		/* ----------------- */
		/// <summary>
		/// Outputs stored input data set; initially read from input data file, but
		/// may be reordered or pruned if desired by a particular application. 
		/// </summary>

		public virtual void outputDataArray()
		{
			if (isPrunedFlag)
			{
				Console.WriteLine("DATA SET (Ordered and Pruned)\n" + "-----------------------------");
			}
		else
		{
			if (isOrderedFlag)
			{
				Console.WriteLine("DATA SET (Ordered)\n" + "------------------");
			}
			else
			{
				Console.WriteLine("DATA SET\n" + "--------");
			}
		}

		// Loop through data array
			for (int index = 0;index < dataArray.Length;index++)
			{
			outputItemSet(dataArray[index]);
			Console.WriteLine();
			}
		}

		/// <summary>
		/// Outputs the given array of array of short integers. <P> Used for
		/// diagnostic purposes. </summary>
		/// <param name="dataSet"> the five array of arrays.  </param>

		protected internal virtual void outputDataArray(short[][] dataSet)
		{
			if (dataSet == null)
			{
			Console.WriteLine("null");
			return;
			}

		// Loop through data array
			for (int index = 0;index < dataSet.Length;index++)
			{
			outputItemSet(dataSet[index]);
			Console.WriteLine();
			}
		}

		/* -------------- */
		/* OUTPUT ITEMSET */
		/* -------------- */
		/// <summary>
		/// Outputs a given item set. </summary>
		/// <param name="itemSet"> the given item set.  </param>

		protected internal virtual void outputItemSet(short[] itemSet)
		{
		// Check for empty set
		if (itemSet == null)
		{
			Console.Write(" null ");
		}
		// Process
		else
		{
			// Reconvert where input dataset has been reordered and possible 
			// pruned.
			short[] tempItemSet = reconvertItemSet(itemSet);
			// Loop through item set elements
				int counter = 0;
			for (int index = 0;index < tempItemSet.Length;index++)
			{
				if (counter == 0)
				{
					counter++;
				Console.Write(" {");
				}
				else
				{
					Console.Write(" ");
				}
				Console.Write(tempItemSet[index]);
			}
			Console.Write("} ");
		}
		}

		/* ---------------------- */
		/* OUTPUT DATA ARRAY SIZE */
		/* ---------------------- */
		/// <summary>
		/// Outputs size (number of records and number of elements) of stored
		/// input data set read from input data file. 
		/// </summary>

		public virtual void outputDataArraySize()
		{
			int numRecords = 0;
		int numElements = 0;

		// Loop through data array

		for (int index = 0;index < dataArray.Length;index++)
		{
			if (dataArray[index] != null)
			{
				numRecords++;
			numElements = numElements + dataArray[index].Length;
			}
		}

		// Output

		Console.WriteLine("Number of records        = " + numRecords);
		Console.WriteLine("Number of elements       = " + numElements);
		double density = (double) numElements / (numCols * numRecords);
		Console.WriteLine("Data set density   = " + twoDecPlaces(density) + "%");
		}

		/* ------------------------ */
		/* OUTPUT CONVERSION ARRAYS */
		/* ------------------------ */
		/// <summary>
		/// Outputs conversion array (used to renumber columns for input data
		/// in terms of frequency of single attributes --- reordering will enhance
		/// performance for some ARM algorithms). 
		/// </summary>

		public virtual void outputConversionArrays()
		{

			// Conversion array
			Console.WriteLine("Conversion Array = ");
		for (int index = 1;index < conversionArray.Length;index++)
		{
			Console.WriteLine("(" + index + ") " + conversionArray[index][0] + " = " + conversionArray[index][1]);
		}

			// Reconversion array
			Console.WriteLine("Reonversion Array = ");
		for (int index = 1;index < reconversionArray.Length;index++)
		{
			Console.WriteLine("(" + index + ") " + reconversionArray[index]);
		}
		}

		/* ----------- */
		/* OUTPUT MENU */
		/* ----------- */
		/// <summary>
		/// Outputs menu for command line arguments. </summary>

		protected internal virtual void outputMenu()
		{
			Console.WriteLine();
		Console.WriteLine("-C  = Confidence (default 80%)");
		Console.WriteLine("-F  = File name");
		Console.WriteLine("-N  = Number of classes (Optional)");
		Console.WriteLine("-S  = Support (default 20%)");
		Console.WriteLine();

		// Exit

		Environment.Exit(1);
		}

		/* --------------- */
		/* OUTPUT SETTINGS */
		/* --------------- */
		/// <summary>
		/// Outputs command line values provided by user. </summary>

		protected internal virtual void outputSettings()
		{
			Console.WriteLine("SETTINGS\n--------");
		Console.WriteLine("File name                = " + fileName);
		Console.WriteLine("Support (default 20%)    = " + support);
		Console.WriteLine("Confidence (default 80%) = " + confidence);
		Console.WriteLine();
		}

		/* OUTPUT SETTINGS */
		/// <summary>
		/// Outputs instance field values. </summary>

		protected internal virtual void outputSettings2()
		{
			Console.WriteLine("SETTINGS\n--------");
			Console.WriteLine("Number of records        = " + numRows);
		Console.WriteLine("Number of columns        = " + numCols);
		Console.WriteLine("Support (default 20%)    = " + support);
		Console.WriteLine("Confidence (default 80%) = " + confidence);
			Console.WriteLine("Min support              = " + minSupport + " (records)");
		Console.WriteLine("Num one itemsets         = " + numOneItemSets);
		}

		/* -------------------------------------- */
		/* OUTPUT SUPPORT AND CONFIDENCE SETTINGS */
		/* -------------------------------------- */
		/// <summary>
		/// Outputs current support and confidence settings. </summary>

		public virtual void outputSuppAndConf()
		{
		Console.WriteLine("Support = " + twoDecPlaces(support) + ", Confidence = " + twoDecPlaces(confidence));
		}

		/* ------------------------ */
		/* OUTPUT RULE LINKED LISTS */
		/* ------------------------ */	

		/// <summary>
		/// Outputs contents of rule linked list (if any) asuming that the list
		/// represents a set of ARs.	
		/// </summary>

		public virtual void outputRules()
		{
			Console.WriteLine("\nASSOCIATION RULES\n=================");
			outputRules(startRulelist);
		}

		/// <summary>
		/// Outputs given rule list. </summary>
		/// <param name="ruleList"> the given rule list.  </param>

		public  void outputRules(RuleNode ruleList)
		{
		// Check for empty rule list
		if (ruleList == null)
		{
			Console.WriteLine("No rules generated!");
		}

		// Loop through rule list
		int number = 1;
			RuleNode linkRuleNode = ruleList;
		while (linkRuleNode != null)
		{
			Console.Write("(" + number + ") ");
			outputRule(linkRuleNode);
				Console.WriteLine(" " + twoDecPlaces(linkRuleNode.confidenceForRule) + "%");
			number++;
			linkRuleNode = linkRuleNode.next;
		}
		}

		/// <summary>
		/// Outputs a rule asuming that the rule represents an ARs. </summary>
		/// <param name="rule"> the rule to be output.  </param>

		private void outputRule(RuleNode rule)
		{
			outputItemSet(rule.antecedent);
		Console.Write(" -> ");
			outputItemSet(rule.consequent);
		}

		/* OUTPUT RULE LINKED LIST WITH DEFAULT */
		/// <summary>
		/// Outputs contents of rule linked list (if any), with reconversion, such
		/// that last rule is the default rule. 
		/// </summary>

		public virtual void outputRulesWithDefault()
		{
			int number = 1;
			RuleNode linkRuleNode = startRulelist;

		while (linkRuleNode != null)
		{
			// Output rule number
			Console.Write("(" + number + ") ");
			// Output antecedent
			if (linkRuleNode.next == null)
			{
				Console.Write("Default -> ");
			}
			else
			{
				outputItemSet(linkRuleNode.antecedent);
				Console.Write(" -> ");
			}
			// Output concequent
				outputItemSet(linkRuleNode.consequent);
				Console.WriteLine(" " + twoDecPlaces(linkRuleNode.confidenceForRule) + "%");
			// Increment parameters
			number++;
			linkRuleNode = linkRuleNode.next;
		}
		}

		/* --------------------------------- */
		/*                                   */
		/*        DIAGNOSTIC OUTPUT          */
		/*                                   */
		/* --------------------------------- */

		/* OUTPUT DURATION */
		/// <summary>
		/// Outputs difference between two given times. </summary>
		/// <param name="time1"> the first time. </param>
		/// <param name="time2"> the second time. </param>
		/// <returns> duration.  </returns>

		public virtual double outputDuration(double time1, double time2)
		{
			double duration = (time2 - time1) / 1000;
		Console.WriteLine("Generation time = " + twoDecPlaces(duration) + " seconds (" + twoDecPlaces(duration / 60) + " mins)");

		// Return
		return (duration);
		}

		/* -------------------------------- */
		/*                                  */
		/*        OUTPUT UTILITIES          */
		/*                                  */
		/* -------------------------------- */

		/* TWO DECIMAL PLACES */

		/// <summary>
		/// Converts given real number to real number rounded up to two decimal 
		/// places. </summary>
		/// <param name="number"> the given number. </param>
		/// <returns> the number to two decimal places.  </returns>

		protected internal virtual double twoDecPlaces(double number)
		{
			int numInt = (int)((number + 0.005) * 100.0);
		number = ((double) numInt) / 100.0;
		return (number);
		}
	}


}